#pragma once

#include <iostream>
#include <assert.h>
#include <functional>
#include "Epoller.hpp"
#include "Sock.hpp"

const static int defaultport = 8888;
const static int gnum = 64;


class EpollServer
{
    using func_t = std::function<std::string (std::string )>;
public:
    EpollServer(func_t func, int port = defaultport):port_(port), func_(func)
    {}

    void InitServer()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();

        epoller_.Create();
        logMessage(Debug, "init server success");
    }

    void Start()
    {
        //1.将listensock.fd添加到epoll中去
        bool ret = epoller_.AddEvent(listensock_.Fd(), EPOLLIN);//EPOLLIN  & EPOLLOUT
        assert(ret);
        (void)ret;

        while(1)
        {
            //2.开始监听
            int timeout = 1000;//这个timeout和poll的作用一样
            int n = epoller_.Wait(revs_, gnum, timeout);
            switch (n)
            {
            case 0:
                logMessage(Debug, "timeout...");
                break;
            case -1:
                logMessage(Warning, "epoll_wait failed");
                break;
            default:
                logMessage(Debug, "有%d个事件就绪了", n);
                HandlerEvents(n);
                break;
            }
        }
    }
    void HandlerEvents(int num)
    {
        //走到这，说明一定有就绪事件
        std::cout << num << std::endl;
        for(int i = 0; i < num; ++i)
        {
            //判断是什么事件就绪
            int fd = revs_[i].data.fd;
            uint32_t events = revs_[i].events;

            logMessage(Debug, "当前正在处理%d上的%s", fd, (events&EPOLLIN) ? "EPOLLIN" : "OTHER");
            if(events == EPOLLIN)
            {
                //读
                if(fd == listensock_.Fd())
                {
                    //是新的连接就绪,Accept()
                    std::string clientip;
                    uint16_t clientport;
                    int sock = listensock_.Accept(&clientip, &clientport);
                    if (sock < 0)
                        continue;
                    logMessage(Debug, "%s:%d 已经连上了服务器了", clientip.c_str(), clientport);
                    // 1.1 此时在这里，我们能不能进行recv/read ? 不能，只有epoll知道sock上面的事件情况，将sock添加到epoll中
                    bool r = epoller_.AddEvent(sock, EPOLLIN);
                    assert(r);
                    (void)r;
                }
                else
                {
                    //可读数据就绪
                    // 我们目前无法保证我们读到一个完整的报文
                    // 为什么？完整报文由应用层协议规定, 本质就是你没有应用层协议！
                    // 怎办？自定义应用层协议
                    char request[1024];
                    ssize_t s = recv(fd, request, sizeof(request) - 1, 0); 
                    if (s > 0)
                    {
                        request[s-1] = 0; // \r\n
                        request[s-2] = 0; // \r\n

                        std::string response = func_(request);//用户自己调用回调函数去处理数据

                        send(fd, response.c_str(), response.size(), 0);
                    }
                    else
                    {
                         if (s == 0)
                            logMessage(Info, "client quit ...");
                        else
                            logMessage(Warning, "recv error, client quit ...");
                        // 在处理异常的时候，先从epoll中移除，然后再关闭,理由如下：
                    //未定义行为： 一旦关闭了文件描述符，它可能被操作系统重新分配给另一个打开的文件，导致你尝试从 epoll 中移除一个错误的文件描述符。这可能会导致未定义行为，程序崩溃或其他不可预测的情况。
                    // 可能的资源泄漏： 如果你在关闭文件描述符之前没有从 epoll 模型中移除它，它可能会继续占用 epoll 实例的资源，从而导致资源泄漏。
                        epoller_.DelEvent(fd);
                        close(fd);
                    }
                }
            }
            else
            {
                //写
            }
        }
    }

    ~EpollServer()
    {
        listensock_.Close();
    }
private:
    uint16_t port_;
    Sock listensock_;
    Epoller epoller_;
    struct epoll_event revs_[gnum];
    func_t func_;
};